﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;
using System.Dynamic;
using System.Linq;
using System.Reflection;
using System.Text;
using PleaseORM.Exceptions;
using PleaseORM.Extensions;
using PleaseORM.Interfaces;
using PleaseORM.Structs;

namespace PleaseORM
{
	public class Get : DynamicObject
	{
		protected IPlease _please { get; set; }

		public Get(IPlease please)
		{
			_please = please;
		}

		public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
		{
			if (binder.Name.IsPlural())
			{
				return GetList(binder, args, out result);
			}
			
			return GetSingle(binder, args, out result);
		}

		private bool GetList(InvokeMemberBinder binder, object[] args, out object result)
		{
			var entityInfo = GetEntityInfo(binder.Name);
			var query = "SELECT * FROM {0}".Change(binder.Name);
			var command = new SqlCommand(query);
			var reader = _please.Configuration.Database.ExecuteReader(command);
			var schemaTable = reader.GetSchemaTable();

			if (reader.HasRows)
			{
				var listEntities = new List<dynamic>();

				while (reader.Read())
				{
					dynamic entity = _please.Configuration.Domain.CreateInstance(entityInfo.EntityFullName);
					foreach (var entityMember in from DataRow row in schemaTable.Rows select EntityMember.Make(reader, row))
					{
						entity.SetValueMember(entityMember.Member, entityMember.Value);
					}

					listEntities.Add(entity);
				}

				result = listEntities as List<dynamic>;
				return true;
			}

			result = new List<DynamicObject>() as IEnumerable<DynamicObject>;
			return true;
		}

		private bool GetSingle(InvokeMemberBinder binder, object[] args, out object result)
		{
			var entityInfo = GetEntityInfo(binder.Name);

			if (entityInfo.EntityName != null)
			{
				dynamic entity = _please.Configuration.Domain.CreateInstance(entityInfo.EntityFullName);
				var query = string.Format("SELECT * FROM {0} WHERE Id = @id", entityInfo.EntityName.MakePlural());
				var command = new SqlCommand(query);
				command.Parameters.Add("@id", args[0]);

				var reader = _please.Configuration.Database.ExecuteReader(command);
				var schemaTable = reader.GetSchemaTable();

				if (reader.HasRows)
				{
					reader.Read();
					foreach (var entityMember in from DataRow row in schemaTable.Rows select EntityMember.Make(reader, row))
					{
						entity.SetValueMember(entityMember.Member, entityMember.Value);
					}
				}

				result = entity;
				return true;
			}
			else
			{
				result = null;
				throw new EntityNotExistException(binder.Name);
			}
		}

		private EntityInfo GetEntityInfo(string methodCall)
		{
			return _please.Configuration.Domain
				.EntityList.Where(x => x.EntityName.Equals(methodCall) 
														|| x.EntityPluralName.Equals(methodCall)).FirstOrDefault();
		}
	}
}
